/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004-2005  Benoit Blanchon                      */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/

// Windows' headers
#include <windows.h>
#include <commctrl.h>
#include <windowsx.h>

// standard headers
#include <tchar.h>

// module's interface
#include "RuleDlg.h"

// project's headers
#include "Assert.h"
#include "Condition.h"
#include "Language.h"
#include "Reason.h"
#include "Resources.h"
#include "Rule.h"


/******************************************************************/
/* Internal constants                                             */
/******************************************************************/

#define WM_UPDATE_TAB	(WM_USER+11)
#define WM_SETPARAMPTR	(WM_USER+12)
#define WM_SAVEPARAM	(WM_USER+13)


/******************************************************************/
/* Internal data types                                            */
/******************************************************************/

typedef struct 
{
  UINT nType ;
  TCHAR szValue[1024] ;
} PARAM ;

typedef struct 
{
  PARAM	* pParam ;
  HWND hwndType ;
} WNDDATA ;


/******************************************************************/
/* Internal functions                                             */
/******************************************************************/

UINT _RuleDlg_GetParamSize (PARAM * p) ;

CONDITION * _RuleDlg_AllocAndSetCond (UINT nReason, UINT nParams, PARAM * pParams) ;




BOOL CALLBACK _RuleDlg_ParamDlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{
  WNDDATA	*pData = (WNDDATA*) GetWindowLong (hDlg, GWL_USERDATA) ;

  switch( message )
    {
    case WM_INITDIALOG:

      // alloc window data
      pData = calloc (1, sizeof(WNDDATA)) ;
      SetWindowLong (hDlg, GWL_USERDATA, (LONG)pData) ;

      // init static data
      pData->hwndType = GetDlgItem (hDlg, IDC_TYPE) ;
    
      SendMessage (hDlg, WM_LANGUAGECHANGED, 0, 0) ;

      return TRUE ;

    case WM_DESTROY:
      
      free (pData) ;
      
      return TRUE ;

    case WM_LANGUAGECHANGED:
      
      // set strings
      ComboBox_AddString (pData->hwndType, STR_DEF(_ANY_VALUE,TEXT("Any value"))) ;
      ComboBox_AddString (pData->hwndType, STR_DEF(_INTEGER,TEXT("Integer"))) ;
      ComboBox_AddString (pData->hwndType, STR_DEF(_STRING,TEXT("String"))) ;
      ComboBox_AddString (pData->hwndType, STR_DEF(_WILDCARDS,TEXT("Wildcards"))) ;
      
      return TRUE ;

    case WM_SETPARAMPTR:

      pData->pParam = (PARAM*) lParam ;

      ComboBox_SetCurSel (pData->hwndType, pData->pParam->nType) ;
      SetDlgItemText (hDlg, IDC_VALUE, pData->pParam->szValue) ;

      return TRUE ;

    case WM_SAVEPARAM:

      if( pData->pParam ) {
	pData->pParam->nType = ComboBox_GetCurSel (pData->hwndType) ;
	GetDlgItemText (hDlg, IDC_VALUE, pData->pParam->szValue, 1024) ;
      }

      return TRUE ;
    }

  return FALSE ; 
}


BOOL CALLBACK RuleDlg_DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) 
{
  static HWND		g_hwndTabCtl ;
  static HWND		g_hwndReason ;
  static HWND		g_hwndParam ;
  static HINSTANCE	g_hInstance ;
  static PARAM		g_aParams[MAX_PARAMS] ;
  static RULE		*g_pRule; 

  UINT		nParam, nParams, nReason ;
  TC_ITEM tie; 
  NMHDR * pnmh ;
  CONDITION *pOldCond, *pNewCond ;

  switch( message )
    {
    case WM_INITDIALOG:

      g_hInstance = (HINSTANCE) GetWindowLong (hDlg, GWL_HINSTANCE) ;

      g_pRule = (RULE*)lParam ;
      ASSERT (g_pRule!=NULL) ;

      g_hwndTabCtl = GetDlgItem (hDlg, IDC_PARAMS) ;
      g_hwndReason = GetDlgItem (hDlg, IDC_REASON) ;
     
      g_hwndParam = CreateDialog (g_hInstance, MAKEINTRESOURCE(DLG_PARAM), 
				  hDlg, _RuleDlg_ParamDlgProc) ;

      for( nReason=0 ; nReason<_REASON_COUNT ; nReason++ )
	ComboBox_AddString (g_hwndReason, Reason_GetName(nReason)) ;

      // if condition already initialized
      if( g_pRule->pCondition )
	{
	  ASSERT (g_pRule->pCondition->nReason<_REASON_COUNT) ;
	  
	  for( nParam=0 ; nParam<Condition_GetParamCount(g_pRule->pCondition) ; nParam++ )
	    {
	      g_aParams[nParam].nType = Condition_GetParamType (g_pRule->pCondition, nParam) ;
	      Condition_GetParamAsString (g_pRule->pCondition, nParam, g_aParams[nParam].szValue, 1024) ;
	    }

	  ComboBox_SetCurSel (g_hwndReason, g_pRule->pCondition->nReason) ; 
	}
      // else no existing condition
      else
	{
	  ZeroMemory (g_aParams, sizeof(g_aParams)) ; 
	  
	  ComboBox_SetCurSel (g_hwndReason, 0) ; 
	}
           
      // reaction
      CheckDlgButton (hDlg, IDC_ACCEPT,	g_pRule->nReaction==RULE_ACCEPT	? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton (hDlg, IDC_FEIGN,	g_pRule->nReaction==RULE_FEIGN	? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton (hDlg, IDC_REJECT, g_pRule->nReaction==RULE_REJECT	? BST_CHECKED : BST_UNCHECKED) ;

      // verbosity
      CheckDlgButton (hDlg, IDC_SILENT,	g_pRule->nVerbosity==RULE_SILENT? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton (hDlg, IDC_LOG,	g_pRule->nVerbosity==RULE_LOG	? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton (hDlg, IDC_ALERT,	g_pRule->nVerbosity==RULE_ALERT	? BST_CHECKED : BST_UNCHECKED) ;

      // options
      CheckDlgButton (hDlg, IDC_ASK,	g_pRule->nOptions&RULE_ASK	? BST_CHECKED : BST_UNCHECKED) ;
      CheckDlgButton (hDlg, IDC_SCAN,	g_pRule->nOptions&RULE_SCAN	? BST_CHECKED : BST_UNCHECKED) ;

      SendMessage (hDlg, WM_LANGUAGECHANGED, 0, 0) ;
      SendMessage (hDlg, WM_UPDATE_TAB, 0, 0) ;

      return TRUE ;

    case WM_LANGUAGECHANGED:

      if( Language_IsLoaded() )
	{
	  SetWindowText (hDlg, STR(_RULE)) ;
	  SetDlgItemText (hDlg, IDT_REASON, STR(_REASON)) ;
	  SetDlgItemText (hDlg, IDT_REACTION, STR(_REACTION)) ;
	  SetDlgItemText (hDlg, IDC_ACCEPT, STR(_ACCEPT)) ;
	  SetDlgItemText (hDlg, IDC_FEIGN, STR(_FEIGN)) ;
	  SetDlgItemText (hDlg, IDC_REJECT, STR(_REJECT)) ;
	  SetDlgItemText (hDlg, IDT_VERBOSITY, STR(_VERBOSITY)) ;
	  SetDlgItemText (hDlg, IDC_SILENT, STR(_SILENT)) ;
	  SetDlgItemText (hDlg, IDC_LOG, STR(_LOG)) ;
	  SetDlgItemText (hDlg, IDC_ALERT, STR(_ALERT)) ;
	  SetDlgItemText (hDlg, IDT_OPTIONS, STR(_OPTIONS)) ;
	  SetDlgItemText (hDlg, IDC_SCAN, STR(_VIRUS_SCAN)) ;
	  SetDlgItemText (hDlg, IDOK, STR(_OK)) ;
	  SetDlgItemText (hDlg, IDCANCEL, STR(_CANCEL)) ;
	  SetDlgItemText (hDlg, IDHELP, STR(_HELP)) ;
	}

      return TRUE ;

    case WM_UPDATE_TAB:

      TabCtrl_DeleteAllItems (g_hwndTabCtl) ;

      nReason = ComboBox_GetCurSel (g_hwndReason) ;
      nParams = Reason_GetParamCount(nReason) ;

      for( nParam=0 ; nParam<nParams ; nParam++)
	{
	  tie.mask	= TCIF_TEXT | TCIF_IMAGE ; 
	  tie.iImage	= -1; 
	  tie.pszText	= (LPTSTR)Reason_GetParamName(nReason, nParam) ; 
	  TabCtrl_InsertItem (g_hwndTabCtl, nParam, &tie); 
	}

      if( Reason_GetParamCount(nReason)>0 )
	{
	  SetWindowPos (g_hwndParam, HWND_TOP, 
		       0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE);
	  ShowWindow (g_hwndParam, SW_SHOW) ;
	  nParam = TabCtrl_GetCurSel(g_hwndTabCtl) ;
	  SendMessage (g_hwndParam, WM_SETPARAMPTR, 0, (LPARAM)&g_aParams[nParam]) ;
	}
      else
	ShowWindow (g_hwndParam, SW_HIDE) ;

      if( Reason_GetOptionMask(nReason) & RULE_SCAN )
	{
	  EnableWindow (GetDlgItem(hDlg,IDC_SCAN), TRUE) ;
	  CheckDlgButton (hDlg, IDC_SCAN,
			  g_pRule->nOptions&RULE_SCAN?
			  BST_CHECKED : BST_UNCHECKED) ;
	}
      else
	{
	  CheckDlgButton (hDlg, IDC_SCAN, BST_UNCHECKED) ;
	  EnableWindow (GetDlgItem(hDlg,IDC_SCAN), FALSE) ;
	}
     
      return TRUE ;
      

    case WM_NOTIFY:
      
      pnmh = (NMHDR*)lParam ;
      
      switch( pnmh->code )
	{
	case TCN_SELCHANGE:
	  
	  nParam = TabCtrl_GetCurSel(g_hwndTabCtl) ;
	  SendMessage (g_hwndParam, WM_SAVEPARAM, 0, 0) ;
	  SendMessage (g_hwndParam, WM_SETPARAMPTR, 0, (LPARAM)&g_aParams[nParam]) ;
	 
	  return TRUE ;
	}
      
      return FALSE ;
      
    case WM_COMMAND:
    
      switch( LOWORD(wParam) )
	{
	case IDC_REASON:
	  	  
	  if( HIWORD(wParam)==CBN_SELCHANGE )
	    SendMessage (hDlg, WM_UPDATE_TAB, 0, 0) ;	      

	  return TRUE ;

	case IDOK:

	  SendMessage (g_hwndParam, WM_SAVEPARAM, 0, 0) ;

	  nReason = ComboBox_GetCurSel (g_hwndReason) ;
	  nParams = Reason_GetParamCount(nReason) ;
	  pOldCond = g_pRule->pCondition ;
	  pNewCond = _RuleDlg_AllocAndSetCond (nReason, nParams, g_aParams) ;

	  if( pNewCond ) {
	    g_pRule->pCondition = pNewCond ;
	    free (pOldCond) ;
	  }

	  // reaction
	  if( IsDlgButtonChecked(hDlg, IDC_ACCEPT)==BST_CHECKED )	g_pRule->nReaction = RULE_ACCEPT ; 
	  if( IsDlgButtonChecked(hDlg, IDC_FEIGN)==BST_CHECKED )	g_pRule->nReaction = RULE_FEIGN ;
	  if( IsDlgButtonChecked(hDlg, IDC_REJECT)==BST_CHECKED )	g_pRule->nReaction = RULE_REJECT ;
	  
	  // verbosity
	  if( IsDlgButtonChecked(hDlg, IDC_SILENT)==BST_CHECKED )	g_pRule->nVerbosity = RULE_SILENT ;
	  if( IsDlgButtonChecked(hDlg, IDC_LOG)==BST_CHECKED )		g_pRule->nVerbosity = RULE_LOG ;
	  if( IsDlgButtonChecked(hDlg, IDC_ALERT)==BST_CHECKED )	g_pRule->nVerbosity = RULE_ALERT ;

	  // options
	  g_pRule->nOptions = 0 ;
	  if( IsDlgButtonChecked(hDlg, IDC_ASK)==BST_CHECKED )		g_pRule->nOptions |= RULE_ASK ;		 
	  if( IsDlgButtonChecked(hDlg, IDC_SCAN)==BST_CHECKED )		g_pRule->nOptions |= RULE_SCAN ;
      
	case IDCANCEL:

	  EndDialog (hDlg, LOWORD(wParam)) ;
	  return TRUE ;

	} // switch( LOWORD(wParam) )
      
      return FALSE ; // case WM_COMMAND:
    }

  return FALSE ;
}


/******************************************************************/
/* Internal function : GetParamSize                               */
/******************************************************************/

UINT _RuleDlg_GetParamSize (PARAM * p) 
{
  UINT nSize = 2*sizeof(UINT) ;

  switch( p->nType )
    {
    case PARAM_ANY:
      break ;

    case PARAM_UINT:
      nSize += sizeof(UINT) ;
      break ;
    
    case PARAM_STRING:
    case PARAM_WILDCARDS:
      nSize += ((p->szValue?_tcslen(p->szValue):0)+1)*sizeof(TCHAR) ;
      break ;

    default:
      ASSERT (!"[Invalid condition format]") ;
    }

  return nSize ;
}



CONDITION * _RuleDlg_AllocAndSetCond (UINT nReason, UINT nParams, PARAM * pParams) 
{
  CONDITION * pCond ;
  PARAM	* pInParam ;
  CONDITION_PARAM * pOutParam ;
  INT nSize ;
  UINT iParam ;
  UINT nOutParamSize ;
  UINT nOutParamOff = 0 ;
  UINT nOutValue ;	
  TCHAR * szOutValue ;
  
  // calc size
  nSize =  sizeof(CONDITION) - sizeof(BYTE) ;
  for( iParam=0 ; iParam<nParams ; iParam++ )
    nSize += _RuleDlg_GetParamSize (&pParams[iParam]) ;
 
  // alloc
  pCond = (CONDITION*) malloc (nSize) ;
  if( ! pCond ) return NULL ;

  // fill size field
  pCond->nSize = nSize ;
  pCond->nReason = nReason ;
  pCond->nParams = nParams ;

  // decrement remaining size
  nSize -= sizeof(CONDITION) - sizeof(BYTE) ; 

  // for each param (begin)
  for( iParam=0 ; iParam<nParams ; iParam++ )
    {
      pInParam = &pParams[iParam] ;
      
      switch( pInParam->nType )
	{
	case PARAM_ANY:
	  
	  // calc size
	  nOutParamSize = 2 * sizeof(UINT) ;
	  nSize -= nOutParamSize ;
	  ASSERT (nSize>=0) ;
	  
	  // add entry
	  pCond->nParamsOffs[iParam] = nOutParamOff ;
	  pOutParam = (CONDITION_PARAM *)( pCond->data + nOutParamOff ) ;
	  nOutParamOff += nOutParamSize ;

	  // fill PARAM
	  pOutParam->nSize	= nOutParamSize ;
	  pOutParam->nType	= PARAM_ANY ;

	  break ;
	  

	case PARAM_UINT:
	  
	  // read value
	  nOutValue = _ttoi (pInParam->szValue) ;

	  // calc size
	  nOutParamSize = 3 * sizeof(UINT) ;
	  nSize -= nOutParamSize ;
	  ASSERT (nSize>=0) ;
	  
	  // add entry
	  pCond->nParamsOffs[iParam] = nOutParamOff ;
	  pOutParam = (CONDITION_PARAM *)( pCond->data + nOutParamOff ) ;
	  nOutParamOff += nOutParamSize ;

	  // fill PARAM
	  pOutParam->nSize	= nOutParamSize ;
	  pOutParam->nType	= PARAM_UINT ;
	  pOutParam->nValue	= nOutValue ;

	  break ;

	case PARAM_STRING:
	case PARAM_WILDCARDS:

	  // get value
	  szOutValue = pInParam->szValue ;

	  // calc size
	  nOutParamSize = 2*sizeof(UINT) + ((szOutValue?_tcslen(szOutValue):0)+1)*sizeof(TCHAR) ;
	  nSize -= nOutParamSize ;
	  ASSERT (nSize>=0) ;
	  
	  // add entry
	  pCond->nParamsOffs[iParam] = nOutParamOff ;
	  pOutParam = (CONDITION_PARAM *)( pCond->data + nOutParamOff ) ;
	  nOutParamOff +=  nOutParamSize ;

	  // fill PARAM
	  pOutParam->nSize	= nOutParamSize ;
	  pOutParam->nType	= pInParam->nType ;
	  //_tcscpy (pOutParam->szValue, szOutValue) ;
	  if( szOutValue ) _tcscpy (pOutParam->szValue, szOutValue) ; 	  
	  else pOutParam->szValue[0] = 0 ;	  

	  break ;
	  
	default:
	  ASSERT (!"[Invalid condition format]") ;
	}
    }

  ASSERT (nSize==0) ;
  
  return pCond ;
}
